//call_once即单例设计模式，一种常见设计模式，用于确保某个类智能创建一个实例。
//由于单例实例是全局唯一的，因此在多线程环境中使用单例模式时需要考虑线程安全问题
//call_once只能在线程函数中使用，不能在main函数中使用

//static修饰的静态变量，只能在static修饰的函数中使用，static变量是程序一开始就创建的，程序结束时才释放



#include<iostream>
#include<thread>
#include<mutex>
#include<string>

class Log
{
public:
	Log() {};
	Log(const Log& log) = delete;	//拷贝构造
	Log& operator=(const Log& log) = delete;	//禁用等号

	//饿汉模式
	
	//static Log& GetInstance()
	//{
	//	static Log log;	
	//	return log;		//当需要使用log日志的时候直接调用GetInstance方法，会自动返回一个静态log对象
	//}
	

	//懒汉模式	提前不声明对象，当需要时才创建
	static Log& GetInstance()
	{
		static Log* log = nullptr;
		
		if (!log)
			log = new Log;
		return *log;
	}



	void PrintLog(std::string msg)
	{
		std::cout <<__TIME__<<' ' << msg << std::endl;
	}




};


void func()
{
	Log::GetInstance().PrintLog("error");
}

int main()
{
	
	std::thread t1(func);
	std::thread t2(func);
	//直接执行，并行的t1和t2会同时创建对象log，会被new两次声明两次，出现问题
	t1.join();
	t2.join();

	return 0;
}













/*
//函数原型
template<class Callable, class... Args>
void call_once(std::once_flag& flag, Callable&& f, Args&&... args);

flag：	标志对象，用于指示 f 是否已调用过。
f：		要调用的可调用对象。
args：	传递给 f 的参数。

//作用:保证可调用对象 f 只被执行一次，即使同时从多个线程调用。
*/




#include<iostream>
#include<thread>
#include<mutex>		//call_once 头文件
#include<string>

static Log* log = nullptr;

static std::once_flag flag;		//call_once创建标志对象
class Log
{
public:
	Log() {};
	Log(const Log& log) = delete;	
	Log& operator=(const Log& log) = delete;	

	static Log& GetInstance()
	{
		
		std::call_once(flag, init);		//设置成单例模式,参数是标志对象，函数名，函数参数

		return *log;
	}

	static void init()
	{
		if (!log)
			log = new Log;
	}


	void PrintLog(std::string msg)
	{
		std::cout <<__TIME__<<' ' << msg << std::endl;
	}

};


void func()
{
	Log::GetInstance().PrintLog("error");
}

int main()
{

	std::thread t1(func);
	std::thread t2(func);

	t1.join();
	t2.join();

	return 0;
}




/*
注意事项:

如果在调用 std::call_once 的时刻，flag 指示 f 已经调用过，那么 std::call_once 会立即返回。
如果在调用 f 时抛出了异常，那么异常将传播给 std::call_once 的调用方，并且 flag 不会被翻转。
如果调用正常返回，那么 flag 被翻转，并保证以同一 flag 对 std::call_once 的其他调用立即返回。
如果有多个线程同时在 flag 未翻转时调用 std::call_once，那么这些调用将被组成单独全序，并被依次执行。
*/

                         


                            




//示例
#include <cstdio>
#include <mutex>
#include <thread>

std::once_flag flag1, flag2;

void simple_do_once()
{
	std::call_once(flag1, []() { printf("only call once\n"); });
}

void may_throw_function(bool do_throw)
{
	if (do_throw) {
		printf("throw, try again...\n");
		throw std::exception();
	}
	printf("no throw, call once\n");
}

void do_once(bool do_throw)
{
	try {
		std::call_once(flag2, may_throw_function, do_throw);
	}
	catch (...) {
	}
}

int main()
{
	std::thread st1(simple_do_once);
	std::thread st2(simple_do_once);
	st1.join();
	st2.join();

	std::thread t1(do_once, true);
	std::thread t2(do_once, false);
	std::thread t3(do_once, true);
	t1.join();
	t2.join();
	t3.join();
}


/*
示例运行结果：
only call once
throw, try again...
throw, try again...
no throw, call once


或者:
only call once
throw, try again...
no throw, call once
*/

/*
版权声明：本文部分内容为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。
                        
原文链接：https://blog.csdn.net/luohaha66/article/details/134506284
*/